#include "CPlayer.h"
#include "GL\glut.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <algorithm>

#define MAX_ROOT 15
#define MAX_SEC 8
#define MAX_OTHER 2
#define R_M 0.7 //Probablility of the root landing on a main beat
#define R_S 1.0//0.85
#define S_M 0.5
#define S_S 1.0//0.75
#define E_M 0.33
#define E_S 0.67
#define MIN_AD 40 //Min/max of the attack/decay of notes
#define MAX_AD 87


CPlayer::CPlayer()
{
	playStr = "";
	strValid = false;
	chord = 1;
	root = C;
	attrs.push_back(0.5f);
	attrs.push_back(0.5f);
	attrs.push_back(0.5f);
	attrs.push_back(0.5f);
	attrs.push_back(1.0f);
	attrs.push_back(0.5f);
	instr = "I49";//I42";
	mode = "Major";
	prog = 0;
	progMode = true;

	srand((unsigned)time(NULL));

	for (int i=0; i<8; i++)
	{
		//Add 16 empty note vectors to hold notes later.
		std::vector<note> beat;
		measure.push_back(beat);
	}
}

void CPlayer::ClearMeasure()
{
	for (int i=0; i<(int)measure.size(); i++)
		measure[i].clear();
}

CPlayer::~CPlayer()
{
	ClearMeasure();
	measure.clear();
	attrs.clear();
}

void CPlayer::MakeMeasure()
{
	//smooth, size, segs, tension, change
	float sm = attrs[0];
	float sz = attrs[1];
	float sg = attrs[2];
	float tn = attrs[3];
	float ch = attrs[4]; //Should be 1-6
	if (ch > 7)
	{
		ch = 0; //You're dumb if you get here, just saying.
	}
	float vl = attrs[5];

	if (ch == 0)
	{
		//If ch == 0, that means that the confidence in the torso location was low, or the movements too small
		//In this case, either stop playing or continue the previous chord.
		strValid = false; //For now, stop playing
	}
	else
	{
		//Given root chord and ch (I, IV, V, etc), start the string with the correct key signature
		//std::string keyStr = lookupKey(root, (int)ch);

		/*************************************************************************************************		
		With this new system (lookupAllKeys), we populate the current Chord. Let AddRoots, etc, add actual
		  "Note"s to measure (the vector of vector of notes). 

		Apply any additional effects, if not already completed by Add functions. 

		After the measure is fully created, call the Stringify method to parse the notes into a string. That
		  function will modify playStr. This means that in this function, here, all we need to do is start 
		  the string with the instrument, call the Add functions, and set the string as valid.


		That's it.		
		**************************************************************************************************/


		playStr = instr + " "; //Initialize playStr

		if (!progMode)
			FindMode(); //Determines mode, such as ionian, lydian, etc.
		// mode = "Dorian";
		Key currentChord[] = {C, D, E, F, G, A, B}; //Just to hold the place, lookupAllKeys changes the values

		current = lookupAllKeys(root, mode, currentChord, ch);
		prog = ch;


		//These functions simply add notes
		if (sg != 0) {
			AddRoots(currentChord);
			Add3and5(currentChord);
			AddExtra(currentChord); 
		}


		//Now, apply effects, such as smoothing, etc.
		//Here, or another function? Might be better organized as another function...?
		
		Smooth();
		Length();
		//Volume();//Don't need anymore, taking care of it

		Stringify(); //ENERGIZE

		ClearMeasure();
		strValid = true;
		//MusicNoteLib::MusicStringParser m = mPlayer.Parser(); //Causes error... don't play with it yet.
	}
}


void CPlayer::Play()
{
	//TODO: when to set/reset strValid? just once at beginning, or multiple times?

	MakeMeasure();
	if (strValid && !mPlayer.IsPlaying())
		mPlayer.PlayAsync(playStr);
}

void CPlayer::Smooth()
{
	//Smooth the attack/decay based on sm. 
	float sm = attrs[0];
	int A = (int)minmax(1., 127 - (127*sm), 127.);
	int D = (int)minmax(1., 127 - (127*sm), 127.);
	bool highSm = false;
	if (sm > 0.5) highSm = true;

	A = (int)newScale(MIN_AD, MAX_AD, 0., 127., (float)A);
	D = (int)newScale(MIN_AD, MAX_AD, 0., 127., (float)D);

	for (int i=0; i<(int)measure.size(); i++)
	{
		for (int j=0; j<(int)measure[i].size(); j++)
		{
			measure[i][j].A = A;
			measure[i][j].D = D;
			if (highSm && i<(int)measure.size()-1)
			{
				//See if there's a matching note in the next segment, connect
				for (int z=0; z<(int)measure[i+1].size(); z++) {
					if (NoteEqual(measure[i][j], measure[i+1][z])) {
						measure[i+1].erase(measure[i+1].begin() + z);
						measure[i][j].Dr++; //nom nom nom
					}
				}
			}
		}
	}
}

void CPlayer::Length()
{
	//Lengthen/shorten the notes based on duration, which is in [0.0-1.0]
	float dr = attrs[6];

	//Right now, the maximum # of 1/16 note beats is 7, since we're in half a measure
	int D = (int)minmax(1., (8*dr), 8.);

	for (int i=0; i<(int)measure.size(); i++)
	{
		for (int j=0; j<(int)measure[i].size(); j++)
		{
			if (i == 0 && D < 8.)
				measure[i][j].Dr = minmax(4., D, 7.);
			measure[i][j].Dr = min(D, D-i);
		}
	}
}

void CPlayer::Volume()
{
	//Not sure how to deal with this now...
}

//Generates a # between 0 and 1. 
float rd()
{
    return rand() / float(RAND_MAX);
}

int ri(int min, int max)
{
	return rand() % (max - min + 1) + min;
}


//Attempts to add the given note to measure[i]. If the note already exists, return
//  false and do not add. Otherwise, return true.
bool CPlayer::Add(note n, int m, bool cont)
{
	if (m < (int)measure.size()) {
		for (int i=0; i<(int)measure[m].size(); i++)
		{
			if (NoteEqual(n, measure[m][i]))
			{
				//Chance of just adding a neighbor octave note
				float r = rd();
				if (r < 0.25 && cont) {
					n.Oct++;
					return Add(n, m, false); }
				else if (r < 0.5 && cont) {
					n.Oct--;
					return Add(n, m, false); }
				else
					return false;
			}
		}
		measure[m].push_back(n);
		return true;
	}
	return false;
}

void CPlayer::AddRoots(Key* chord)
{
	//Given the root key at index 0 of the chord, put root notes in the measure.
	//The main and secondary beats in the measure are more likely to contain root notes.

	float smooth = attrs[0];
	float size = attrs[1];
	float segs = attrs[2];


	float adjusted = min(MAX_ROOT, segs);
	int num = ri(1,(int)adjusted); //number of root notes to add. Minimum = 1.
	if (num < 0) num = 1; //why would this even be the case

	for (int i=0; i<num; i++)
	{
		//Now, add that many notes. 
		int center = 5; //Octave 5 = center. Adjust later if adding more variables.

		//Set up the generic note, add effects later.
		note n;
		n.k = chord[0];
		int octave = (int)attrs[7];

		if (rd() > 0.5) {
			if (size > 0.85)
				octave += ri(0,4);
			else if (size > 0.7)
				octave += ri(0,3); 
			else if (size > 0.25)
				octave += ri(0,2); }
		else {
			if (size > 0.6)
				octave -= ri(0,4);
			else if (size > 0.4)
				octave -= ri(0,3); 
			else if (size > 0.25)
				octave -= ri(0,2); }

		n.Oct = octave;
		n.A = 64;
		n.D = 64;


		//TODO: Adjust these weights
		float r = rd();
		bool main = false, sec = false;
		if (r < R_M) //Try main beats first
			main = true;
		if (r < R_S) //Else, secondary beats first
			sec = true;

		bool complete = false;
		if (main)
		{
			if (!Add(n, 0, true))
				complete = false;//Add(n, 8);
		}

		if (complete && rd()<0.75) continue;

		if (sec)
		{
			//First try 4, 12
			if (!Add(n, 4, true))
				complete = false;//Add(n, 12);
			if (complete && rd()<0.5) continue;

			//But if not complete, try 2, 6, 10, 14, in randomized order
			int ar[] = {2,6};//,10,14};
			std::vector<int> ints (ar, ar + sizeof(ar) / sizeof(int) );
			std::random_shuffle(ints.begin(), ints.end());

			for (int j=0; j<(int)ints.size(); j++)
			{
				if (Add(n, ints[j], true))
				{
					complete = true;
					break;
				}
			}
		}

		/*if (complete && rd()<0.5) continue;
		else
		{
			//If still not complete, try 1,3,5,7,9,11,13,15 in randomized order
			int ar[] = {1,3,5,7};//9,11,13,15};
			std::vector<int> ints (ar, ar + sizeof(ar) / sizeof(int) );
			std::random_shuffle(ints.begin(), ints.end());

			for (int j=0; j<(int)ints.size(); j++)
			{
				if (Add(n, ints[j], true))
				{
					complete = true;
					break;
				}
			}
		}*/
		

	}

}


void CPlayer::Add3and5(Key* chord)
{
	//Given the 3rd and 5th notes that complete the basic chord (major, minor), add these to the measure.
	//These can go wherever...? 
	
	float smooth = attrs[0];
	float size = attrs[1];
	float segs = attrs[2];


	float adjusted = min(MAX_SEC, segs);
	int num = ri(1,(int)adjusted); //number of root notes to add. Minimum = 1.
	if (num < 0) num = 1;

	int notes[] = {2, 4};

	for (int i=0; i<num; i++)
	{
		//Now, add that many notes. 
		int center = 5; //Octave 5 = center. Adjust later if adding more variables.

		//Set up the generic note, add effects later.
		note n;
		n.k = chord[notes[ri(0,1)]];
		int octave = (int)attrs[7]; //attrs[7] is between 0 and 0.8, about

		if (rd() > 0.5) {
			if (size > 0.6)
				octave += ri(0,3);
			else if (size > 0.4)
				octave += ri(0,2); 
			else if (size > 0.25)
				octave += ri(0,1); }
		else {
			if (size > 0.6)
				octave -= ri(0,3);
			else if (size > 0.4)
				octave -= ri(0,2); 
			else if (size > 0.25)
				octave -= ri(0,1); }

		n.Oct = octave;
		n.A = 64;
		n.D = 64;


		//TODO: Adjust these weights
		float r = rd();
		bool main = false, sec = false;
		if (r < S_M) //Try main beats first
			main = true;
		if (r < S_S) //Secondary beats first
			sec = true;



		bool complete = false;
		if (main)
		{
			if (!Add(n, 0, true))
				complete = false;//Add(n, 8);
		}

		if (complete && rd()<0.75) continue;

		if (sec)
		{
			//First try 4, 12
			if (!Add(n, 4, true))
				complete = false;//Add(n, 12);
			if (complete && rd()<0.5) continue;

			//But if not complete, try 2, 6, 10, 14, in randomized order
			int ar[] = {2,6};//,10,14};
			std::vector<int> ints (ar, ar + sizeof(ar) / sizeof(int) );
			std::random_shuffle(ints.begin(), ints.end());

			for (int j=0; j<(int)ints.size(); j++)
			{
				if (Add(n, ints[j], true))
				{
					complete = true;
					break;
				}
			}
		}

		/*if (complete && rd()<0.5) continue;
		else
		{
			//If still not complete, try 1,3,5,7,9,11,13,15 in randomized order
			int ar[] = {1,3,5,7};//,9,11,13,15};
			std::vector<int> ints (ar, ar + sizeof(ar) / sizeof(int) );
			std::random_shuffle(ints.begin(), ints.end());

			for (int j=0; j<(int)ints.size(); j++)
			{
				if (Add(n, ints[j], true))
				{
					complete = true;
					break;
				}
			}
		}*/
		

	}
}


void CPlayer::AddExtra(Key* chord)
{
	//Add the extra keys not addressed by the other Add functions. 
}



//Done, except for tweaking.
//If tweaking, change the thresholds 0.4, 0.6... maybe.
void CPlayer::FindMode()
{
	//Based on tension alone? 
	//0.0 - 0.4 tension = Major
	//0.4 - 0.6 tension = Tends to maintain previous mode
	//0.6 - 1.0 tension = Minor

	float tension = attrs[3];
	
	bool minor = false;

	if (tension > 0.4 && tension < 0.6)
	{
		//If in the middle, some likelihood of just remaining with the previous.
		//This way we don't get rapid switching between minor/major.
		float r = rd();
		if (r < .4)
		{
			if (mode == "Lydian" || mode == "Major")
				minor = false;
			else
				minor = true;
		}
		else if (tension >= 0.5) //Split down the middle?
			minor = true; //Maintain tension's original value
	}


	if (tension >= 0.6 || minor)
	{
		//Minor, pick a mode

		if (mode == "Aeolian" || mode == "Phrygian" || mode == "Dorian")
		{
			//If previous mode was also minor, high chance to stay in the same mode
			//This way it doesn't switch too often and sound strange
			float r = rd();
			if (r > .1)
				return; //Continues if fails random check
		}

		//If wasn't aready minor, set it to random minor mode. Decide later whether to regard one
		//   as "more" minor that the others. 
		float r = rd();
		if (r <= .33)
			mode = "Aeolian";
		else if (r < .67)
			mode = "Phrygian";
		else
			mode = "Dorian";
		return;
	}
	else 
	{
		//Major, pick a mode
		
		if (mode == "Lydian" || mode == "Major")
		{
			//If previous mode was also major, high chance to stay in the same mode
			//This way it doesn't switch too often and sound strange
			float r = rd();
			if (r > .1)
				return; //Continues if fails random check
		}
		
		//Should one be "more" major than the other? 
		float r = rd();
		if (r <= .4)
			mode = "Lydian";
		else
			mode = "Major";//Technically Ionian, but w/e...
		return;
	}
}



void CPlayer::Stringify()
{
	/*
	Note:
	   A = Attack.		int [0-127]
	   D = Decay.		int [0-127]
	   Oct = Octave.	int [0-10]
	   k = Key.			enum [A-Ab]
	
	Individual notes should be structured as
		k (Oct)# [Duration] A# D# [Connectors?]

	After each beat in the measure, must add string inidicator of new channel.	
	*/

	for (int i=0; i<(int)measure.size(); i++)
	{
		//Initialize this channel
		if (i != 9)
			playStr += "V" + itos(i) + " " + instr + " " + addWait(i, true) + " ";
		else
			playStr += "V0 RHS ";

		for (int n=0; n<(int)measure[i].size(); n++)
		{
			//Now add all the notes to the string
			playStr += noteToString(measure[i][n]) + " ";
		}

		//Clean up this channel?
	}

	int a = 1;
	a++;
}




//Draw-- puts the current chord and mode on the screen
//Might want to give a background.... or don't care. HA
void CPlayer::Draw(int winX, int winY)
{
	glColor3f(0., 1., 1.);
	if (mode == "Phrygian" || mode == "Dorian" || mode == "Aeolian")
		printw(30., winY - 50., 0., "Current mode is minor (" + mode + ")");
	else
		printw(30., winY - 50., 0., "Current mode is major (" + mode + ")");

	std::string toPrint = "Root Key [" + keyToString(root) + "]";
	if (prog != 0)
		toPrint += " - " + roman(prog) + " [" + keyToString(current) + "]";
	printw(30., winY - 90., 0., toPrint);

}